<template>
  <view class="uni-data-checklist">
    <template v-if="loading">
      <view class="uni-data-loading">
        <uni-load-more
          status="loading"
          icon-type="snow"
          :icon-size="18"
          :content-text="contentText"
        />
      </view>
    </template>
    <template v-else>
      <checkbox-group
        v-if="multiple"
        class="checklist-group"
        :class="{'is-list':mode==='list','is-wrap':wrap}"
        @change="chagne"
      >
        <label
          v-for="(item,index) in dataList"
          :key="index"
          class="checklist-box"
          :class="item.labelClass"
          :style="[item.styleBackgroud]"
        >
          <checkbox
            class="hidden"
            hidden
            :disabled="!!item.disabled"
            :value="item.value+''"
            :checked="item.selected"
          />
          <view
            v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')"
            class="checkbox__inner"
            :class="item.checkboxBgClass"
            :style="[item.styleIcon]"
          >
            <view
              class="checkbox__inner-icon"
              :class="item.checkboxClass"
            />
          </view>
          <view
            class="checklist-content"
            :class="{'list-content':mode === 'list' && icon ==='left'}"
          >
            <text
              class="checklist-text"
              :class="item.textClass"
              :style="[item.styleIconText]"
            >{{ item.text }}</text>
            <view
              v-if="mode === 'list' && icon === 'right'"
              class="checkobx__list"
              :class="item.listClass"
              :style="[item.styleBackgroud]"
            />
          </view>
        </label>
      </checkbox-group>
      <radio-group
        v-else
        class="checklist-group"
        :class="{'is-list':mode==='list','is-wrap':wrap}"
        @change="chagne"
      >
        <label
          v-for="(item,index) in dataList"
          :key="index"
          class="checklist-box"
          :class="item.labelClass"
          :style="[item.styleBackgroud]"
        >
          <radio
            hidden
            :disabled="item.disabled"
            :value="item.value+''"
            :checked="item.selected"
          />
          <view
            v-if="(mode !=='tag' && mode !== 'list') || ( mode === 'list' && icon === 'left')"
            class="radio__inner"
            :class="item.checkboxBgClass"
            :style="[item.styleBackgroud]"
          >
            <view
              class="radio__inner-icon"
              :class="item.checkboxClass"
              :style="[item.styleIcon]"
            />
          </view>
          <view
            class="checklist-content"
            :class="{'list-content':mode === 'list' && icon ==='left'}"
          >
            <text
              class="checklist-text"
              :class="item.textClass"
              :style="[item.styleIconText]"
            >{{ item.text }}</text>
            <view
              v-if="mode === 'list' && icon === 'right'"
              class="checkobx__list"
              :class="item.listClass"
              :style="[item.styleRightIcon]"
            />
          </view>
        </label>
      </radio-group>
    </template>
  </view>
</template>

<script>
import { defineComponent } from "vue";

/**
 * DataChecklist 数据选择器
 * @description 通过数据渲染 checkbox 和 radio
 * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
 * @property {String} mode = [default| list | button | tag] 显示模式
 * @value default  	默认横排模式
 * @value list		列表模式
 * @value button	按钮模式
 * @value tag 		标签模式
 * @property {Boolean} multiple = [true|false] 是否多选
 * @property {Array|String|Number} value 默认值
 * @property {Array} localdata 本地数据 ，格式 [{text:'',value:''}]
 * @property {Number|String} min 最小选择个数 ，multiple为true时生效
 * @property {Number|String} max 最大选择个数 ，multiple为true时生效
 * @property {Boolean} wrap 是否换行显示
 * @property {String} icon = [left|right]  list 列表模式下icon显示位置
 * @property {Boolean} selectedColor 选中颜色
 * @property {Boolean} selectedTextColor 选中文本颜色，如不填写则自动显示
 * @value left 左侧显示
 * @value right 右侧显示
 * @event {Function} change  选中发生变化触发
 */

export default defineComponent({
  name: "UniDataChecklist",
  props: {
    mode: {
      type: String,
      default: "default",
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    value: {
      type: [Array, String, Number],
      default() {
        return "";
      },
    },
    localdata: {
      type: Array,
      default() {
        return [];
      },
    },
    min: {
      type: [Number, String],
      default: "",
    },
    max: {
      type: [Number, String],
      default: "",
    },
    wrap: {
      type: Boolean,
      default: false,
    },
    icon: {
      type: String,
      default: "left",
    },
    selectedColor: {
      type: String,
      default: "",
    },
    selectedTextColor: {
      type: String,
      default: "",
    },
    // clientDB 相关
    options: {
      type: [Object, Array],
      default() {
        return {};
      },
    },
    collection: {
      type: String,
      default: "",
    },
    action: {
      type: String,
      default: "",
    },
    field: {
      type: String,
      default: "",
    },
    pageData: {
      type: String,
      default: "add",
    },
    pageCurrent: {
      type: Number,
      default: 1,
    },
    pageSize: {
      type: Number,
      default: 20,
    },
    getcount: {
      type: [Boolean, String],
      default: false,
    },
    orderby: {
      type: String,
      default: "",
    },
    where: {
      type: [String, Object],
      default: "",
    },
    getone: {
      type: [Boolean, String],
      default: false,
    },
    manual: {
      type: Boolean,
      default: false,
    },
  },
  emits: ["input", "change"],
  data() {
    return {
      dataList: [],
      range: [],
      contentText: {
        contentdown: "查看更多",
        contentrefresh: "加载中",
        contentnomore: "没有更多",
      },
      styles: {
        selectedColor: "#30475d",
        selectedTextColor: "#333",
      },
    };
  },
  watch: {
    localdata: {
      handler(newVal) {
        this.range = newVal;
        this.dataList = this.getDataList(this.getSelectedValue(newVal));
      },
      deep: true,
    },

    listData(newVal) {
      this.range = newVal;
      this.dataList = this.getDataList(this.getSelectedValue(newVal));
    },
    value(newVal) {
      this.dataList = this.getDataList(newVal);
      this.formItem && this.formItem.setValue(newVal);
    },
  },
  created() {
    this.form = this.getForm("uniForms");
    this.formItem = this.getForm("uniFormsItem");
    this.formItem && this.formItem.setValue(this.value);
    this.styles = {
      selectedColor: this.selectedColor,
      selectedTextColor: this.selectedTextColor,
    };

    if (this.formItem) {
      if (this.formItem.name) {
        this.rename = this.formItem.name;
        this.form.inputChildrens.push(this);
      }
    }

    if (this.localdata && this.localdata.length !== 0) {
      this.range = this.localdata;
      this.dataList = this.getDataList(this.getSelectedValue(this.range));
    } else {
      if (this.collection) {
        this.loadData();
      }
    }
  },
  methods: {
    init(range) {},
    /**
     * 获取父元素实例
     */
    getForm(name = "uniForms") {
      let parent = this.$parent;
      let parentName = parent.$options.name;
      while (parentName !== name) {
        parent = parent.$parent;
        if (!parent) return false;
        parentName = parent.$options.name;
      }
      return parent;
    },
    chagne(e) {
      const values = e.detail.value;

      let detail = {
        value: [],
        data: [],
      };

      if (this.multiple) {
        this.range.forEach((item) => {
          if (values.includes(item.value + "")) {
            detail.value.push(item.value);
            detail.data.push(item);
          }
        });
      } else {
        const range = this.range.find((item) => item.value + "" === values);
        if (range) {
          detail = {
            value: range.value,
            data: range,
          };
        }
      }
      this.formItem && this.formItem.setValue(detail.value);
      this.$emit("input", detail.value);
      this.$emit("change", {
        detail,
      });
      if (this.multiple) {
        // 如果 v-model 没有绑定 ，则走内部逻辑
        // if (this.value.length === 0) {
        this.dataList = this.getDataList(detail.value, true);
        // }
      } else {
        this.dataList = this.getDataList(detail.value);
      }
    },
    getLabelClass(item, index) {
      let classes = [];
      switch (this.mode) {
        case "default":
          item.disabled && classes.push("disabled-cursor");
          break;
        case "button":
          classes.push(...["is-button", ...this.getClasses(item, "button")]);
          break;
        case "list":
          if (this.multiple) {
            classes.push("is-list-multiple-box");
          } else {
            classes.push("is-list-box");
          }
          item.disabled && classes.push("is-list-disabled");
          index !== 0 && classes.push("is-list-border");
          break;
        case "tag":
          classes.push(...["is-tag", ...this.getClasses(item, "tag")]);
          break;
      }
      classes = classes.join(" ");
      return classes;
    },
    getCheckboxClass(item, type = "") {
      let classes = [];
      if (this.multiple) {
        classes.push(...this.getClasses(item, "default-multiple", type));
      } else {
        classes.push(...this.getClasses(item, "default", type));
      }
      classes = classes.join(" ");
      return classes;
    },
    getTextClass(item) {
      let classes = [];
      switch (this.mode) {
        case "default":
          classes.push(...this.getClasses(item, "list"));
          break;
        case "button":
          classes.push(...this.getClasses(item, "list"));
          break;
        case "list":
          classes.push(...this.getClasses(item, "list"));
          break;
        case "tag":
          classes.push(
            ...["is-tag-text", ...this.getClasses(item, "tag-text")]
          );
          break;
      }
      classes = classes.join(" ");
      return classes;
    },

    /**
     * 获取渲染的新数组
     * @param {Object} value 选中内容
     */
    getDataList(value) {
      // 解除引用关系，破坏原引用关系，避免污染源数据
      let dataList = JSON.parse(JSON.stringify(this.range));
      let list = [];
      if (this.multiple) {
        if (!Array.isArray(value)) {
          value = [];
          // console.error('props 类型错误');
        }
      }
      dataList.forEach((item, index) => {
        item.disabled = item.disable || item.disabled || false;
        if (this.multiple) {
          if (value.length > 0) {
            let have = value.find((val) => val === item.value);
            item.selected = have !== undefined;
          } else {
            item.selected = false;
          }
        } else {
          item.selected = value === item.value;
        }

        list.push(item);
      });
      return this.setRange(list);
    },
    /**
     * 处理最大最小值
     * @param {Object} list
     */
    setRange(list) {
      let selectList = list.filter((item) => item.selected);
      let min = Number(this.min) || 0;
      let max = Number(this.max) || "";
      list.forEach((item, index) => {
        if (this.multiple) {
          if (selectList.length <= min) {
            let have = selectList.find((val) => val.value === item.value);
            if (have !== undefined) {
              item.disabled = true;
            }
          }

          if (selectList.length >= max && max !== "") {
            let have = selectList.find((val) => val.value === item.value);
            if (have === undefined) {
              item.disabled = true;
            }
          }
        }
        this.setClass(item, index);
        list[index] = item;
      });
      return list;
    },
    /**
     * 设置 class
     * @param {Object} item
     * @param {Object} index
     */
    setClass(item, index) {
      // 设置 label 的 class
      item.labelClass = this.getLabelClass(item, index);
      // 设置 checkbox外层样式
      item.checkboxBgClass = this.getCheckboxClass(item, "-bg");
      // 设置 checkbox 内层样式
      item.checkboxClass = this.getCheckboxClass(item);
      // 设置文本样式
      item.textClass = this.getTextClass(item);
      // 设置 list 对勾右侧样式
      item.listClass = this.getCheckboxClass(item, "-list");

      //  设置自定义样式
      item.styleBackgroud = this.setStyleBackgroud(item);
      item.styleIcon = this.setStyleIcon(item);
      item.styleIconText = this.setStyleIconText(item);
      item.styleRightIcon = this.setStyleRightIcon(item);
    },
    /**
     * 获取 class
     */
    getClasses(item, name, type = "") {
      let classes = [];
      item.disabled && classes.push("is-" + name + "-disabled" + type);
      item.selected && classes.push("is-" + name + "-checked" + type);

      if (this.mode !== "button" || name === "button") {
        item.selected &&
          item.disabled &&
          classes.push("is-" + name + "-disabled-checked" + type);
      }

      return classes;
    },
    /**
     * 获取选中值
     * @param {Object} range
     */
    getSelectedValue(range) {
      if (!this.multiple) return this.value;
      let selectedArr = [];
      range.forEach((item) => {
        if (item.selected) {
          selectedArr.push(item.value);
        }
      });
      return this.value.length > 0 ? this.value : selectedArr;
    },

    /**
     * 设置背景样式
     */
    setStyleBackgroud(item) {
      let styles = {};

      if (item.selected) {
        if (this.mode !== "list") {
          styles.borderColor = this.styles.selectedColor;
        }
        if (this.mode === "tag") {
          styles.backgroundColor = this.styles.selectedColor;
        }
      }
      return styles;
    },
    setStyleIcon(item) {
      let styles = {};

      if (item.selected) {
        styles.backgroundColor = this.styles.selectedColor;
        styles.borderColor = this.styles.selectedColor;
      }
      return styles;
    },
    setStyleIconText(item) {
      let styles = {};
      if (item.selected) {
        if (this.styles.selectedTextColor) {
          styles.color = this.styles.selectedTextColor;
        } else {
          if (this.mode === "tag") {
            styles.color = "#fff";
          } else {
            styles.color = this.styles.selectedColor;
          }
        }
      }
      return styles;
    },
    setStyleRightIcon(item) {
      let styles = {};
      if (item.selected) {
        if (this.mode === "list") {
          styles.borderColor = this.styles.selectedColor;
        }
      }

      return styles;
    },
  },
});
</script>

<style>
.uni-data-checklist {
  position: relative;
  z-index: 0;
  /* min-height: 36px; */
}

.uni-data-loading {
  display: flex;
  align-items: center;
  /* justify-content: center; */
  height: 36px;
  padding-left: 10px;
}

.checklist-group {
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  flex-direction: row;
  flex-wrap: wrap;
}

.checklist-box {
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  flex-direction: row;
  align-items: center;
  margin: 5px 0;
  margin-right: 25px;
}

.checklist-text {
  font-size: 14px;
  color: #333;
  margin-left: 5px;
  transition: color 0.2s;
}

.is-button {
  margin-right: 10px;
  padding: 3px 15px;
  border: 1px #dcdfe6 solid;
  border-radius: 3px;
  transition: border-color 0.2s;
}

.is-list {
  flex-direction: column;
}

.is-list-box {
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  padding: 10px 15px;
  padding-left: 0;
  margin: 0;
}

.checklist-content {
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  flex: 1;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}

.list-content {
  margin-left: 5px;
}

.is-list-multiple-box {
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  padding: 10px 15px;
  padding-left: 0;
  margin: 0;
}

.is-list-border {
  border-top: 1px #eee solid;
}

.is-tag {
  margin-right: 10px;
  padding: 3px 10px;
  border: 1px #eee solid;
  border-radius: 3px;
  background-color: #f5f5f5;
  /* transition: border-color 0.1s; */
}

.is-tag-text {
  margin: 0;
  color: #666;
}

.checkbox__inner {
  flex-shrink: 0;
  position: relative;
  border: 1px solid #dcdfe6;
  border-radius: 2px;
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  background-color: #fff;
  z-index: 1;
  transition: border-color 0.1s;
}

.checkbox__inner-icon {
  border: 1px solid #fff;
  border-left: 0;
  border-top: 0;
  height: 8px;
  left: 5px;
  position: absolute;
  top: 1px;
  width: 3px;
  opacity: 0;
  transition: transform 0.2s;
  transform-origin: center;
  transform: rotate(40deg) scaleY(0.4);
}

.radio__inner {
  flex-shrink: 0;
  /* #ifndef APP-NVUE */
  display: flex;
  /* #endif */
  justify-content: center;
  align-items: center;
  position: relative;
  border: 1px solid #dcdfe6;
  border-radius: 2px;
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  border-radius: 16px;
  background-color: #fff;
  z-index: 1;
  transition: border-color 0.3s;
}

.radio__inner-icon {
  width: 8px;
  height: 8px;
  border-radius: 10px;
  opacity: 0;
  transition: transform 0.3s;
}

.checkobx__list {
  border: 1px solid #fff;
  border-left: 0;
  border-top: 0;
  height: 12px;
  width: 6px;
  transform-origin: center;
  opacity: 0;
  transition: all 0.3s;
  transform: rotate(45deg);
}

/* 禁用样式 */
.is-default-disabled-bg {
  background-color: #f2f6fc;
  border-color: #dcdfe6;
  /* #ifdef H5 */
  cursor: not-allowed;
  /* #endif */
}

.is-default-multiple-disabled-bg {
  background-color: #f2f6fc;
  border-color: #dcdfe6;
  /* #ifdef H5 */
  cursor: not-allowed;
  /* #endif */
}

.is-default-disabled {
  border-color: #f2f6fc;
}

.is-default-multiple-disabled {
  border-color: #f2f6fc;
}

.is-list-disabled {
  /* #ifdef H5 */
  cursor: not-allowed;
  /* #endif */
  color: #999;
}

.is-list-disabled-checked {
  color: #a1dcc1;
}

.is-button-disabled {
  /* #ifdef H5 */
  cursor: not-allowed;
  /* #endif */
  border-color: #ebeef5;
}

.is-button-text-disabled {
  color: #c0c4cc;
}

.is-button-disabled-checked {
  border-color: #a1dcc1;
}

.is-tag-disabled {
  /* #ifdef H5 */
  cursor: not-allowed;
  /* #endif */
  border-color: #e9e9eb;
  background-color: #f4f4f5;
}

.is-tag-text-disabled {
  color: #bcbec2;
}

/* 选中样式 */
.is-default-checked-bg {
  border-color: #30475d;
}

.is-default-multiple-checked-bg {
  border-color: #30475d;
  background-color: #30475d;
}

.is-default-checked {
  opacity: 1;
  background-color: #30475d;
  transform: rotate(45deg) scaleY(1);
}

.is-default-multiple-checked {
  opacity: 1;
  transform: rotate(45deg) scaleY(1);
}

.is-default-disabled-checked-bg {
  opacity: 0.4;
}

.is-default-multiple-disabled-checked-bg {
  opacity: 0.4;
}

.is-default-checked-list {
  border-color: #30475d;
  opacity: 1;
  transform: rotate(45deg) scaleY(1);
}

.is-default-multiple-checked-list {
  border-color: #30475d;
  opacity: 1;
  transform: rotate(45deg) scaleY(1);
}

.is-list-disabled-checked {
  opacity: 0.4;
}

.is-default-disabled-checked-list {
  opacity: 0.4;
}

.is-default-multiple-disabled-checked-list {
  opacity: 0.4;
}

.is-button-checked {
  border-color: #30475d;
}

.is-button-disabled-checked {
  opacity: 0.4;
}

.is-list-checked {
  color: #30475d;
}

.is-tag-checked {
  border-color: #30475d;
  background-color: #30475d;
}

.is-tag-text-checked {
  color: #fff;
}

.is-tag-disabled-checked {
  opacity: 0.4;
}

.disabled-cursor {
  /* #ifdef H5 */
  cursor: not-allowed;
  /* #endif */
}

.is-wrap {
  flex-direction: column;
}

.hidden {
  /* #ifdef MP-ALIPAY */
  display: none;
  /* #endif */
}
</style>
